﻿using System;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Threading;

namespace GamingNetCode
{
    public delegate void MessagePasser(string MessageType, params object[] Content);

    public abstract class Client
    {
        protected Thread T;
        protected ManualResetEvent MER;
        protected TcpClient C;
        protected String mUser;
        protected String ConnectionIP;
        protected int Port;

        public event MessagePasser MessageReceived;

        public Client(String ConnectionIP, int Port, String UserName)
        {
            C = new TcpClient();
            this.mUser = UserName;
            this.ConnectionIP = ConnectionIP;
            this.Port = Port;
        }

        public abstract void SendClientCommunication(string MessageType, params object[] Content);

        public void Start()
        {
            C.Connect(new IPEndPoint((ConnectionIP != "localhost") ? IPAddress.Parse(ConnectionIP) : IPAddress.Loopback, Port));
            T = new Thread(this.MessageListener);
            T.Name = "Message Listener Thread (Client)";
            MER = new ManualResetEvent(false);

            T.Start();
        }

        public void Stop()
        {
            this.MER.Set();
            this.C.Close();
            this.T.Join();

            this.MER = null;
            this.C = null;
            this.T = null;
        }

        protected void MessageListener()
        {
            NetworkStream NS = this.C.GetStream();

            while (true)
            {
                try
                {
                    StringBuilder SB = new StringBuilder();

                    for (int i = 0; i < BaseTCPMessage.DATASTARTINDEX; i++)
                    {
                        int c = NS.ReadByte();

                        if (c == -1)
                            break;

                        SB.Append((char)c);
                    }

                    int MessageSize = BaseTCPMessage.GetMessageSize(SB.ToString());

                    for (int i = 0; i < MessageSize - BaseTCPMessage.DATASTARTINDEX; i++)
                    {
                        int c = NS.ReadByte();

                        if (c == -1)
                            break;

                        SB.Append((char)c);
                    }

                    this.HandleMessage(SB.ToString());
                }
                catch
                {
                    break;
                }

                lock (this.MER)
                {
                    if (this.MER.WaitOne(0, false))
                        break;
                }
            }
        }

        protected void HandleMessage(string M)
        {
            this.HandleMessage(BaseTCPMessage.GenerateMessage(M));
        }

        protected abstract void HandleMessage(BaseTCPMessage M);

        protected void WriteMessage(BaseTCPMessage M)
        {
            NetworkStream NS = this.C.GetStream();

            NS.Write(M.EncodeMessage(), 0, BaseTCPMessage.GetMessageSize(M.EncodeMessage()));
        }
    }
}
